import re
import uuid
from datetime import datetime
from threading import Thread
from typing import Any, List

import json
import requests
from fastapi import Depends, APIRouter, FastAPI
from pydantic import BaseModel
from shortuuid import ShortUUID
from sqlalchemy import select, create_engine, update, insert, func
from sqlalchemy.ext.asyncio import AsyncSession
import asyncio

from sqlalchemy.orm import sessionmaker, Session
from sqlalchemy.testing import async_test

from apis.deps import get_db
from models import WeeklyReport, CaseInterfaceList, Host, caseList, TestReport, Project, TestDetail

router = APIRouter()


@router.post('/test')
async def tester_start_interface(db: AsyncSession = Depends(get_db)):
    sql_caseInterafaceList = select(CaseInterfaceList)
    result_caseInterafaceList = await db.execute(sql_caseInterafaceList)
    caseInterafaceList_ = result_caseInterafaceList.scalars().all()

    for caseInterfaceList in caseInterafaceList_:
        request_method = caseInterfaceList.requestMethod
        url = caseInterfaceList.domain + caseInterfaceList.route
        requestParams = caseInterfaceList.requestParams
        try:
            response = getattr(requests, request_method)(url=url, data=requestParams)
        except requests.exceptions.RequestException as e:
            raise e
    return {}


class testCase(BaseModel):
    host: str
    route: str
    requestProtocol: str
    requestMethod: str
    requestParams: Any


session = requests.session()


@router.post('/single_tester')
async def single_tester(test_case: testCase, db: AsyncSession = Depends(get_db)):
    request_method = test_case.requestMethod
    url = test_case.requestProtocol + "://" + test_case.host + test_case.route
    requestParams = test_case.requestParams
    try:
        response = getattr(session, request_method)(url=url, data=requestParams)
    except requests.exceptions.RequestException as e:
        raise e
    return {"code": 200, "msg": "success", "data": response.json()}


def tes_single(test_case={}):
    request_method = test_case['requestMethod']
    url = test_case['requestProtocol'] + "://" + test_case['host'] + test_case['route']
    request_params = test_case['requestParams']
    response = getattr(session, request_method)(url=url, data=request_params)


# tes_single(test_case={"host": "www.wanandroid.com", "request_params": {}, "create_time": "2023-10-11 02:24:05",
#                              "update_time": "2023-12-04 09:39:49", "name": "测试get请求", "description": "成功登陆",
#                              "requestMethod": "get", "route": "/article/list/1/json", "createName": "创建者",
#                              "updateName": "最后更新人", "testResult": {"name": "value"}, "status": True,
#                              "requestProtocol": "http", "domain": "", "isClearCookie": True,
#                              "headers": {"name": "value"},
#                              "requestParams": "{\n  \"username\": \"admin\",\n  \"password\": \"123456\"\n}",
#                              "setGlobalVars": {"name": "value"}, "checkHttpCode": "200", "checkResponseTime": 5,
#                              "checkResponseData": {"name": "value"}, "caseListId": 1, "id": 1})


class Start_test_in(BaseModel):
    caseInterfaceListId: list = []
    caseListId: list = []
    project_id: int = 1
    host: str = ''
    execute_name = ''
    execute_mode = ''


@router.post('/start_test')
async def start_test(data: Start_test_in, db: Session = Depends(get_db)):
    execute_name = data.execute_name
    execute_mode = data.execute_mode
    caseInterfaceListId_list = []
    if data.caseInterfaceListId:
        caseInterfaceListId_list = data.caseInterfaceListId
    elif data.caseListId:
        for caseListId in data.caseListId:
            caseInterfaceListId_list_sql = await db.execute(
                select(CaseInterfaceList.id).where(CaseInterfaceList.caseListId == caseListId))
            caseInterfaceListId_list_ = caseInterfaceListId_list_sql.scalars().all()
            caseInterfaceListId_list.extend(caseInterfaceListId_list_)
    result = await execute_all_test_case(execute_name=execute_name, execute_mode=execute_mode,
                                         caseInterfaceListId=caseInterfaceListId_list, project_id=data.project_id,
                                         host=data.host,
                                         CaseInterfaceList_model=CaseInterfaceList, TestReport_model=TestReport,
                                         TestDetail_model=TestDetail, db=db)
    return {"code": 200, "msg": "测试执行成功"}


return_sql_global_var_list = []


async def execute_all_test_case(execute_name, execute_mode, project_id, host, CaseInterfaceList_model, TestReport_model,
                                TestDetail_model, db, caseInterfaceListId: list = None, ):
    execute_name = execute_name
    execute_mode = execute_mode
    # 测试报告
    if execute_mode != "单用例手动执行":  # execute_mode = ["单用例手动执行","用例组手动执行"]
        test_count = 0
        pass_count = 0
        failed_count = 0
        total_test_spending_time = 0
        project_name_sql = await db.execute(select(Project.name).where(Project.id == project_id))
        project_name = project_name_sql.scalars().one()
        come_from = execute_mode
        await db.execute(
            insert(TestReport_model).values(project_id=project_id, project_name=project_name,
                                            come_from=come_from, execute_nickName=execute_name,
                                            ))
        result = await db.execute(select(func.LAST_INSERT_ID()))
        test_report_id = result.scalar()

    # 根据前端传入的用例id列表，获取测试用例信息
    for caseInterfaceListId in caseInterfaceListId:
        caseInterafaceList = await db.execute(
            select(CaseInterfaceList_model).where(CaseInterfaceList_model.id == caseInterfaceListId,
                                                  CaseInterfaceList_model.status == 1))
        caseInterafaceList_ = caseInterafaceList.scalars().all()

        # 执行接口自动化测试
        for caseInterafaceList_ in caseInterafaceList_:
            requestProtocol = caseInterafaceList_.requestProtocol
            requestMethod = caseInterafaceList_.requestMethod
            domain = caseInterafaceList_.domain
            route = caseInterafaceList_.route
            headers = caseInterafaceList_.headers
            headers = {item['name']: item['value'] for item in headers}
            if domain:
                url = requestProtocol + "://" + domain + route
            elif host:
                url = requestProtocol + "://" + host + route
            request_params = caseInterafaceList_.requestParams

            if request_params:
                request_params = json.loads(caseInterafaceList_.requestParams)
                # 使用正则表达式匹配${}格式的数据
                matches = re.findall(r"\${\w+}", str(request_params))
                # 如果匹配到了${}格式的数据，则进行替换操作
                if matches:
                    global return_sql_global_var_list
                    keys_list = [k for item in return_sql_global_var_list for k in item.keys()]  # 获取所有字典的键组成的列表
                    for match in matches:
                        key = match[2:-1]  # 去掉"${"和"}"，获取键名
                        if key in keys_list:
                            index = next((i for i, sublist in enumerate(keys_list) if key in sublist),
                                         None)  # 找到第一个包含key的字典索引
                            value = str(return_sql_global_var_list[index][key])
                            request_params = str(request_params).replace(match, value)

            set_global_vars = caseInterafaceList_.setGlobalVars
            try:
                response = getattr(session, requestMethod)(url=url, data=(request_params if request_params else None),
                                                           headers=(headers if headers != {'': ''} else None))
            except requests.exceptions.RequestException as e:
                raise e
            # 将请求内容、响应内容内容存入数据库中
            try:
                response_data = response.json()
            except Exception as e:
                response_data = response.text
            status_code = response.status_code
            response_time = response.elapsed.total_seconds()
            total_test_spending_time += response_time
            # 进行断言
            test_result_status = 'success'
            reason_for_failure = []
            if caseInterafaceList_.checkHttpCode:
                if status_code != int(caseInterafaceList_.checkHttpCode):
                    test_result_status = 'failed'
                    reason_for_failure.append(
                        f"Http状态校验失败，期望Http状态码为{caseInterafaceList_.checkHttpCode}，实际Http状态码为{status_code}")
            if caseInterafaceList_.checkResponseTime:
                if response_time > caseInterafaceList_.checkResponseTime:
                    test_result_status = 'failed'
                    reason_for_failure.append(
                        f"接口耗时校验失败，期望接口耗时为{caseInterafaceList_.checkResponseTime}s，实际接口耗时为{response_time}s")
            if caseInterafaceList_.checkResponseData != [{"name": "", "value": ""}]:
                for check_response_data in caseInterafaceList_.checkResponseData:
                    check_response_data_value = check_response_data["value"].split(".")
                    response_data_regular_value = response_data
                    for check_response_data_value_ in check_response_data_value:
                        response_data_regular_value = response_data_regular_value[check_response_data_value_]
                    if str(response_data_regular_value) != check_response_data["name"]:
                        test_result_status = 'failed'
                        reason_for_failure.append(
                            f"JSON正则校验失败，查询语句为{check_response_data.get('value')}，期望正则校验为{check_response_data.get('name')}，实际正则查询为{response_data_regular_value}")
            cookie = response.cookies.get_dict()
            expected_status_code = caseInterafaceList_.checkHttpCode
            expected_response_data = caseInterafaceList_.checkResponseData
            test_count += 1
            if test_result_status == 'success':
                pass_count += 1
            if test_result_status == 'failed':
                failed_count += 1
            # 设置全局变量
            if set_global_vars != [{"name": "", "value": ""}]:
                for set_global_var in set_global_vars:
                    sql_global_var = set_global_var["value"]
                    sql_global_var_list = sql_global_var.split(".")
                    return_sql_global_var = response_data
                    try:
                        for sql_global_var_ in sql_global_var_list:
                            return_sql_global_var = return_sql_global_var[sql_global_var_]
                    except Exception as e:
                        raise e
                    return_sql_global_var_list.append({set_global_var["name"]: return_sql_global_var})

            testResult = {"status_code": status_code, "response_time": response_time,
                          "response_data": response_data, "url": url, "requestMethod": requestMethod,
                          "request_time": datetime.now().strftime('%Y-%m-%d %H:%M:%S'), "headers": headers,
                          "request_data": request_params, "cookie": cookie, "request_params": request_params,
                          "expected_status_code": expected_status_code,
                          "expected_response_data": expected_response_data, "test_result_status": test_result_status,
                          "reason_for_failure": reason_for_failure, "execute_name": execute_name,
                          "execute_mode": execute_mode}
            await db.execute(update(CaseInterfaceList_model).where(
                CaseInterfaceList_model.id == caseInterfaceListId).values(testResult=testResult))
            # 请求结果写入测试报告
            if test_report_id:
                await db.execute(
                    insert(TestDetail_model).values(name=caseInterafaceList_.name,
                                                    requestMethod=requestMethod, url=url, headers=headers,
                                                    cookies=cookie, requestParams=request_params,
                                                    checkHttpCode=caseInterafaceList_.checkHttpCode,
                                                    httpCode=status_code,
                                                    checkResponseData=caseInterafaceList_.checkResponseData,
                                                    testResult=response_data, testConclusion=test_result_status,
                                                    checkResponseTime=caseInterafaceList_.checkResponseTime,
                                                    responseTime=response_time, test_report_id=test_report_id))
    if test_count:
        await db.execute(update(TestReport_model).where(
            TestReport_model.id == test_report_id).values(test_count=test_count, pass_count=pass_count,
                                                          failed_count=failed_count,
                                                          pass_rate="{:.2%}".format(pass_count / test_count),
                                                          total_test_spending_time=total_test_spending_time))

    await db.commit()
    await db.close()
